<?php
/**
 * Soar Framework - Lightweight and simple to use PHP Framework
 *
 * Soar is free software and is distributed WITHOUT ANY WARRANTY
 *
 * @package Soar
 * @version 1.0 $Id: SoarTemplateEngine.php 2 2012-04-06 21:50:33Z shay@shayanderson.com $
 * @copyright Copyright (c) 2012 ShayAnderson.com
 * @license http://www.gnu.org/licenses/gpl.html GPL License
 * @link http://www.shayanderson.com/projects/soar-php-framework.htm
 */

/**
 * Soar Framework Simple Template Engine
 *
 * @package Soar
 * @name SoarTemplateEngine
 * @author Shay Anderson 04.12
 *
 * @example
 * $t = new SoarTemplateEngine();
 * // SoarTemplateEngine::$template_dir = './_app/tpl/';
 * $t->set('myvar', 'example value');
 * $obj = new ExampleClass;
 * $obj->name = 'example';
 * $t->set('myobj', $obj);
 * $arr = array('mykey' => 'myvalue');
 * $t->set('myarr', $arr);
 * $t->display('path/to/template_file');
 * 'template_file' example:
 * {$myvar} <= 'example value'
 * {$myobj->name} <= 'example'
 * {$myarr.mykey} <= 'myvalue'
 * {include=tempate_file2}
 */
final class SoarTemplateEngine
{
	/**
	 * Template vars
	 *
	 * @var array
	 */
	private $__vars = array();

	/**
	 * Template file extension
	 *
	 * @var string
	 */
	public static $file_ext = '.tpl';

	/**
	 * Template base directory
	 *
	 * @var string
	 */
	public static $template_dir;

	/**
	 * Compile template
	 *
	 * @param string $template_file_path
	 * @return string
	 */
	private function __compile($template_file_path)
	{
		$template_file_path = self::__formatTemplateFilename($template_file_path);
		if(!file_exists($template_file_path))
		{
			trigger_error("Failed to find template file \"{$template_file_path}\"",
				SoarErrorHandler::ERROR);
		}
		$parts = preg_split('/(\{include=(?:.*?)\})/', file_get_contents($template_file_path),
			-1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
		foreach($parts as $k => &$part)
		{
			// check for include template file, ex: {include=x}
			if(preg_match('/(?:\{include=(.*?)\})/', $part, $matches))
			{
				if(isset($matches[1]))
				{
					// include template file
					$tmp = new self;
					$tmp->set($this->__vars);
					$part = $tmp->fetch($matches[1]);
					unset($tmp);
				}
			}
			else
			{
				// check to set vars, ex: {$var=value}
				preg_match_all('/\{\\$(\w+)=(.*?)\}/', $part, $matches);
				for($i = 0; $i < count($matches[0]); $i++)
				{
					if(isset($matches[1][$i]))
					{
						$this->__vars[$matches[1][$i]] = isset($matches[2][$i])
							? $matches[2][$i] : NULL;
					}
					$part = str_replace($matches[0][$i], NULL, $part);
				}
				// check to replace vars, ex: {$var}
				preg_match_all('/\{\\$(\w+)\}/', $part, $matches);
				for($i = 0; $i < count($matches[0]); $i++)
				{
					$val = NULL;
					if(isset($matches[1][$i], $this->__vars[$matches[1][$i]]))
					{
						$val = $this->__vars[$matches[1][$i]];
					}
					$part = str_replace($matches[0][$i], $val, $part);
				}
				// check to replace object property, ex: {$obj->prop}
				preg_match_all('/\{\\$(\w+\-\>\w+)\}/', $part, $matches);
				for($i = 0; $i < count($matches[0]); $i++)
				{
					if(isset($matches[1][$i]))
					{
						list($obj, $prop) = explode('->', $matches[1][$i]);
						$val = NULL;
						if(isset($this->__vars[$obj]) && is_object($this->__vars[$obj]) 
							&& isset($this->__vars[$obj]->$prop))
						{
							$val = $this->__vars[$obj]->$prop;
							
						}
						$part = str_replace($matches[0][$i], $val, $part);
					}
				}
				// check to replace array element, ex: {$array.key}
				preg_match_all('/\{\\$(\w+\.\w+)\}/', $part, $matches);
				for($i = 0; $i < count($matches[0]); $i++)
				{
					if(isset($matches[1][$i]))
					{
						list($arr, $key) = explode('.', $matches[1][$i]);
						$val = NULL;
						if(isset($this->__vars[$arr]) && is_array($this->__vars[$arr])
							&& isset($this->__vars[$arr][$key]))
						{
							$val = $this->__vars[$arr][$key];
						}
						$part = str_replace($matches[0][$i], $val, $part);
					}
				}
			}
			// remove if empty line
			$tmp_part = preg_replace('/[\r\n\s]/', NULL, $part);
			if(empty($tmp_part))
			{
				unset($parts[$k]);
			}
			unset($tmp_part);
		}
		return implode(NULL, $parts);
	}

	/**
	 * Format template filename, ex: 'example/file' => './_app/tpl/example/file.tpl'
	 *
	 * @param string $template_file_path
	 * @return string
	 */
	private function __formatTemplateFilename($template_file_path)
	{
		// check for template dir
		if(substr($template_file_path, 0, strlen(self::$template_dir))
			!= self::$template_dir)
		{
			$template_file_path = self::$template_dir . $template_file_path;
		}
		// check for file extension
		if(substr($template_file_path, -(strlen(self::$file_ext)), strlen(self::$file_ext))
			!= self::$file_ext)
		{
			$template_file_path .= self::$file_ext;
		}
		return $template_file_path;
	}

	/**
	 * Echo/display template file
	 *
	 * @param string $template_file_path
	 * @return void
	 */
	public function display($template_file_path)
	{
		echo $this->__compile($template_file_path);
	}

	/**
	 * Get compiled template output
	 *
	 * @param string $template_file_path
	 * @return string
	 */
	public function fetch($template_file_path)
	{
		return $this->__compile($template_file_path);
	}

	/**
	 * Template var getter
	 *
	 * @param string $var
	 * @return string
	 */
	public function get($var)
	{
		if(isset($this->__vars[$var]))
		{
			return $this->__vars[$var];
		}
	}

	/**
	 * Template var setter
	 *
	 * @param string|array $var
	 * @param mixed $value
	 * @return void
	 */
	public function set($var, $value = NULL)
	{
		if(is_array($var))
		{
			foreach($var as $k => $v)
			{
				$this->set($k, $v);
			}
		}
		else
		{
			$this->__vars[$var] = $value;
		}
	}
}
?>